﻿using System;
using Ast.Core.WinApi;
using Ast.Framework;

namespace Ast.Core.Services
{
    public interface IDriverInstaller : IDependency
    {
        bool IsInstalled(string svcName);
        bool Install(string svcDriverPath, string svcDriverName, string svcDisplayName);
        bool Uninstall(string svcName);
        bool Start(string svcName);
        bool Stop(string svcName);
    }

    public class DriverInstaller : IDriverInstaller
    {
        public bool IsInstalled(string svcName)
        {
            var scManagerHandle = Native.OpenSCManager(null, null, Constant.SC_MANAGER_ALL_ACCESS);
            if (IntPtr.Zero == scManagerHandle)
            {
                return false;
            }

            var schDriverService = Native.OpenService(scManagerHandle, svcName, Constant.SERVICE_ALL_ACCESS);
            if (IntPtr.Zero == schDriverService)
            {
                Native.CloseServiceHandle(scManagerHandle);
                return false;
            }

            Native.CloseServiceHandle(schDriverService);
            Native.CloseServiceHandle(scManagerHandle);
            return true;
        }

        public bool Install(string svcDriverPath, string svcDriverName, string svcDisplayName)
        {
            var scManagerHandle = Native.OpenSCManager(null, null, Constant.SC_MANAGER_CREATE_SERVICE);
            if (IntPtr.Zero == scManagerHandle)
            {
                return false;
            }

            if (!IsInstalled(svcDriverName))
            {
                var schDriverService = Native.CreateService(
                    scManagerHandle, svcDriverName, svcDisplayName,
                    Constant.SERVICE_ALL_ACCESS,
                    Constant.SERVICE_KERNEL_DRIVER,
                    Constant.SERVICE_DEMAND_START,
                    Constant.SERVICE_ERROR_NORMAL,
                    svcDriverPath, null,
                    null, null, null, null
                    );

                Native.CloseServiceHandle(scManagerHandle);
                return IntPtr.Zero != schDriverService;
            }

            Native.CloseServiceHandle(scManagerHandle);
            return true;
        }

        public bool Uninstall(string svcName)
        {
            var scManagerHandle = Native.OpenSCManager(null, null, Constant.SC_MANAGER_ALL_ACCESS);
            if (IntPtr.Zero == scManagerHandle)
            {
                return false;
            }

            var schDriverService = Native.OpenService(scManagerHandle, svcName, Constant.SERVICE_ALL_ACCESS);
            if (IntPtr.Zero == schDriverService)
            {
                Native.CloseServiceHandle(scManagerHandle);
                return false;
            }

            Native.DeleteService(schDriverService);
            Native.CloseServiceHandle(schDriverService);
            Native.CloseServiceHandle(scManagerHandle);
            return true;
        }

        public bool Start(string svcName)
        {
            var scManagerHandle = Native.OpenSCManager(null, null, Constant.SC_MANAGER_CREATE_SERVICE);
            if (IntPtr.Zero == scManagerHandle)
            {
                return false;
            }

            var schDriverService = Native.OpenService(scManagerHandle, svcName, Constant.SERVICE_ALL_ACCESS);
            if (IntPtr.Zero == schDriverService)
            {
                Native.CloseServiceHandle(scManagerHandle);
                return false;
            }

            if (!Native.StartService(schDriverService, 0, null))
            {
                Native.CloseServiceHandle(schDriverService);
                Native.CloseServiceHandle(scManagerHandle);
                return false;
            }

            Native.CloseServiceHandle(schDriverService);
            Native.CloseServiceHandle(scManagerHandle);
            return true;
        }

        public bool Stop(string svcName)
        {
            var scManagerHandle = Native.OpenSCManager(null, null, Constant.SC_MANAGER_CREATE_SERVICE);
            if (IntPtr.Zero == scManagerHandle)
            {
                return false;
            }

            var schDriverService = Native.OpenService(scManagerHandle, svcName, Constant.SERVICE_ALL_ACCESS);
            if (IntPtr.Zero == schDriverService)
            {
                Native.CloseServiceHandle(scManagerHandle);
                return false;
            }

            var serviceStatus = new Structure.ServiceStatus();
            if (!Native.ControlService(schDriverService, Constant.SERVICE_CONTROL_STOP, ref serviceStatus))
            {
                Native.CloseServiceHandle(schDriverService);
                Native.CloseServiceHandle(scManagerHandle);
                return false;
            }

            Native.CloseServiceHandle(schDriverService);
            Native.CloseServiceHandle(scManagerHandle);
            return true;
        }
    }
}